﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Vowei.Core.Models;
using Vowei.Data.Models;
using Vowei.Data;
using Vowei.Core.Resource;

namespace vowei.test
{
    [TestClass]
    public class ResourceCalendarTest : TestBase
    {
        [TestMethod]
        public void StandardBaseCalendarDefinitionDesignTest()
        {
            var calendar = CreateStandardResourceCalendar();
            var calc = new WorkingHourCalculator(calendar);

            Assert.IsFalse(calc.IsWorkingTime(new DateTime(2011, 4, 23)));
            Assert.IsTrue(calc.IsWorkingTime(new DateTime(2011, 4, 26)));
            Assert.AreEqual(TimeSpan.FromHours(8), calc.CalcWorkingHours(new DateTime(2011, 4, 26)));
            Assert.AreEqual(TimeSpan.FromHours(8), calc.CalcWorkingHours(new DateTime(2011, 4, 26, 8, 0, 0), new DateTime(2011, 4, 26, 18, 0, 0)));
            Assert.AreEqual(TimeSpan.FromHours(16), calc.CalcWorkingHours(new DateTime(2011, 4, 26, 8, 0, 0), new DateTime(2011, 4, 28, 8, 0, 0)));
            Assert.AreEqual(TimeSpan.FromHours(8), calc.CalcWorkingHours(new DateTime(2010, 12, 13, 10, 0, 0), new DateTime(2010, 12, 14, 10, 0, 0)));
        }

        [TestMethod]
        public void WholeDayBaseCalendarDefinitionDesignTest()
        {
            var calendar = new DbResourceCalendar("24 Hours");
            var workweek = new WorkWeek("Default")
            {
                EffectPeriod = new Period { Start = DateTime.MinValue, Finish = DateTime.MaxValue }
            };

            for (var d = DayOfWeek.Sunday; d <= DayOfWeek.Saturday; ++d)
            {
                var day = new WorkDay(d)
                {
                    Type = WorkingTimeType.Working
                };

                day.Periods.Add(new Period 
                {
                    Start = new DateTime(1, 1, 1, 0, 0, 0),
                    Finish = new DateTime(1, 1, 2, 0, 0, 0, 0)
                });
                workweek.Days.Add(day);
            }

            calendar.WorkWeeks.Add(workweek);
            var calc = new WorkingHourCalculator(calendar);

            Assert.IsTrue(calc.IsWorkingTime(new DateTime(2011, 4, 23)));
            Assert.IsTrue(calc.IsWorkingTime(new DateTime(2011, 4, 26)));
            Assert.AreEqual(TimeSpan.FromHours(24), calc.CalcWorkingHours(new DateTime(2011, 4, 26)));
            Assert.AreEqual(TimeSpan.FromHours(10), calc.CalcWorkingHours(new DateTime(2011, 4, 26, 8, 0, 0), new DateTime(2011, 4, 26, 18, 0, 0)));
        }

        [TestMethod]
        public void WorkHoursCalculationBasicCalendarTest()
        {
            var calendar = CreateStandardResourceCalendar();
            var calc = new WorkingHourCalculator(calendar);

            Assert.AreEqual(TimeSpan.FromHours(41), calc.CalcWorkingHours(new DateTime(2010, 12, 7, 9, 0, 0), new DateTime(2010, 12, 14, 10, 0, 0)));
            Assert.AreEqual(TimeSpan.FromHours(33), calc.CalcWorkingHours(new DateTime(2010, 12, 7, 9, 0, 0), new DateTime(2010, 12, 13, 10, 0, 0)));
            Assert.AreEqual(TimeSpan.FromHours(8), calc.CalcWorkingHours(new DateTime(2011, 4, 18, 8, 0, 0), new DateTime(2011, 4, 18, 18, 0, 0)));
            Assert.AreEqual(TimeSpan.FromHours(8), calc.CalcWorkingHours(new DateTime(2011, 4, 18, 8, 0, 0), new DateTime(2011, 4, 19, 8, 0, 0)));
            Assert.AreEqual(TimeSpan.FromHours(9), calc.CalcWorkingHours(new DateTime(2011, 4, 18, 8, 0, 0), new DateTime(2011, 4, 19, 9, 0, 0)));
        }

        [TestMethod]
        public void WorkHoursCalculationCustomCalendarTest()
        {
            var calendar = CreateStandardResourceCalendar();
            var calc = new WorkingHourCalculator(calendar);
            calendar.WorkWeeks.First().EffectPeriod = new Period
            {
                Start = new DateTime(2011, 4, 1),
                Finish = new DateTime(2011, 4, 29, 23, 59, 59)
            };

            var workweek = new WorkWeek("Additional1")
            {
                EffectPeriod = new Period { Start = new DateTime(2011, 5, 3), Finish = new DateTime(2011, 5, 31) }
            };
            workweek.Days.UnionWith(calendar.WorkWeeks.First().Days);
            calendar.WorkWeeks.Add(workweek);

            workweek = new WorkWeek("Additional2")
            {
                EffectPeriod = new Period { Start = new DateTime(2011, 6, 3), Finish = new DateTime(2011, 6, 30) }
            };
            workweek.Days.UnionWith(calendar.WorkWeeks.First().Days);
            calendar.WorkWeeks.Add(workweek);

            Assert.AreEqual(TimeSpan.Zero, calc.CalcWorkingHours(new DateTime(2010, 12, 7, 9, 0, 0), new DateTime(2010, 12, 14, 10, 0, 0)));
            Assert.AreEqual(TimeSpan.Zero, calc.CalcWorkingHours(new DateTime(2010, 12, 7, 9, 0, 0), new DateTime(2010, 12, 13, 10, 0, 0)));
            Assert.AreEqual(TimeSpan.FromHours(8), calc.CalcWorkingHours(new DateTime(2011, 4, 18, 8, 0, 0), new DateTime(2011, 4, 18, 18, 0, 0)));
            Assert.AreEqual(TimeSpan.FromHours(8), calc.CalcWorkingHours(new DateTime(2011, 4, 18, 8, 0, 0), new DateTime(2011, 4, 19, 8, 0, 0)));
            Assert.AreEqual(TimeSpan.FromHours(9), calc.CalcWorkingHours(new DateTime(2011, 4, 18, 8, 0, 0), new DateTime(2011, 4, 19, 9, 0, 0)));
            Assert.AreEqual(TimeSpan.FromHours(8), calc.CalcWorkingHours(new DateTime(2011, 4, 29, 8, 0, 0), new DateTime(2011, 5, 1, 8, 0, 0)));
            Assert.AreEqual(TimeSpan.Zero, calc.CalcWorkingHours(new DateTime(2011, 4, 30, 8, 0, 0), new DateTime(2011, 5, 3, 8, 0, 0)));
            Assert.AreEqual(TimeSpan.FromHours(1), calc.CalcWorkingHours(new DateTime(2011, 4, 30, 8, 0, 0), new DateTime(2011, 5, 3, 9, 0, 0)));
            Assert.AreEqual(TimeSpan.Zero, calc.CalcWorkingHours(new DateTime(2011, 5, 31, 8, 0, 0), new DateTime(2011, 6, 1, 8, 0, 0)));
        }

        [TestMethod]
        public void BVT_PeriodIntersectTest()
        {
            var target = new Period
            {
                Start = new DateTime(2011, 3, 26, 10, 0, 0),
                Finish = new DateTime(2011, 4, 28, 9, 0, 0)
            };
            // 内置
            var period = new Period
            {
                Start = new DateTime(2011, 4, 1, 9, 0, 0),
                Finish = new DateTime(2011, 4, 15, 8, 0, 0)
            };
            var actual = target.Intersect(period);
            var expected = period;

            Assert.AreEqual(expected.Start, actual.Start);
            Assert.AreEqual(expected.Finish, actual.Finish);

            // 被包含
            period = new Period
            {
                Start = new DateTime(2011, 1, 1, 8, 10, 59),
                Finish = new DateTime(2011, 6, 11, 1, 1, 0)
            };
            actual = target.Intersect(period);
            expected = target;

            Assert.AreEqual(expected.Start, actual.Start);
            Assert.AreEqual(expected.Finish, actual.Finish);

            // 完全相等
            actual = target.Intersect(target);
            expected = target;

            Assert.AreEqual(expected.Start, actual.Start);
            Assert.AreEqual(expected.Finish, actual.Finish);

            // 前面重叠
            period = new Period
            {
                Start = new DateTime(2011, 1, 1, 8, 10, 59),
                Finish = new DateTime(2011, 4, 11, 1, 1, 0)
            };
            actual = target.Intersect(period);
            expected = new Period
            {
                Start = new DateTime(2011, 3, 26, 10, 0, 0),
                Finish = new DateTime(2011, 4, 11, 1, 1, 0)
            }; 

            Assert.AreEqual(expected.Start, actual.Start);
            Assert.AreEqual(expected.Finish, actual.Finish);

            // 后面重叠
            period = new Period
            {
                Start = new DateTime(2011, 4, 1, 9, 0, 0),
                Finish = new DateTime(2011, 6, 11, 1, 1, 0)
            };
            actual = target.Intersect(period);
            expected = new Period
            {
                Start = new DateTime(2011, 4, 1, 9, 0, 0),
                Finish = new DateTime(2011, 4, 28, 9, 0, 0)
            };

            Assert.AreEqual(expected.Start, actual.Start);
            Assert.AreEqual(expected.Finish, actual.Finish);

            // 前面没有交集
            period = new Period
            {
                Start = new DateTime(2010, 4, 1, 9, 0, 0),
                Finish = new DateTime(2010, 4, 28, 9, 0, 0)
            };
            actual = target.Intersect(period);
            Assert.IsNull(actual);

            // 后面没有交集
            period = new Period
            {
                Start = new DateTime(2112, 4, 1, 9, 0, 0),
                Finish = new DateTime(2112, 4, 28, 9, 0, 0)
            };
            actual = target.Intersect(period);
            Assert.IsNull(actual);

            // 时间是0的情况
            target = new Period
            {
                Start = new DateTime(1, 1, 1, 0, 0, 0),
                Finish = new DateTime(1, 1, 2, 0, 0, 0)
            };
            var ptp = new PeriodTimePortion
            {
                Start = new TimeSpan(9, 45, 34),
                Finish = new TimeSpan(10, 0, 0)
            };

            var ptpActual = target.Intersect(ptp);
            var ptpExpected = ptp;

            Assert.AreEqual(ptpExpected.Start, ptpActual.Start);
            Assert.AreEqual(ptpExpected.Finish, ptpActual.Finish);

            // 后面到日期结束
            ptp = new PeriodTimePortion
            {
                Start = new TimeSpan(9, 45, 34),
                Finish = new TimeSpan(0, 0, 0)
            };

            ptpActual = target.Intersect(ptp);
            ptpExpected = ptp;

            Assert.AreEqual(ptpExpected.Start, ptpActual.Start);
            Assert.AreEqual(ptpExpected.Finish, ptpActual.Finish);

            // 前面是日期开始
            ptp = new PeriodTimePortion
            {
                Start = new TimeSpan(0, 0, 0),
                Finish = new TimeSpan(10, 29, 35)
            };

            ptpActual = target.Intersect(ptp);
            ptpExpected = ptp;

            Assert.AreEqual(ptpExpected.Start, ptpActual.Start);
            Assert.AreEqual(ptpExpected.Finish, ptpActual.Finish);

            // 时间内置
            target = new Period
            {
                Start = new DateTime(1, 1, 1, 9, 0, 0),
                Finish = new DateTime(1, 1, 2, 18, 0, 0)
            };
            ptp = new PeriodTimePortion
            {
                Start = new TimeSpan(12, 0, 0),
                Finish = new TimeSpan(13, 0, 0)
            };

            ptpActual = target.Intersect(ptp);
            ptpExpected = ptp;

            Assert.AreEqual(ptpExpected.Start, ptpActual.Start);
            Assert.AreEqual(ptpExpected.Finish, ptpActual.Finish);

            // 被包含
            ptp = new PeriodTimePortion
            {
                Start = new TimeSpan(8, 0, 0),
                Finish = new TimeSpan(19, 0, 0)
            };

            ptpActual = target.Intersect(ptp);
            ptpExpected = new PeriodTimePortion
            {
                Start = target.Start.TimeOfDay,
                Finish = target.Finish.TimeOfDay
            };

            Assert.AreEqual(ptpExpected.Start, ptpActual.Start);
            Assert.AreEqual(ptpExpected.Finish, ptpActual.Finish);

            // 前面重叠
            ptp = new PeriodTimePortion
            {
                Start = new TimeSpan(8, 0, 0),
                Finish = new TimeSpan(12, 0, 0)
            };

            ptpActual = target.Intersect(ptp);
            ptpExpected = new PeriodTimePortion
            {
                Start = new TimeSpan(9, 0, 0),
                Finish = new TimeSpan(12, 0, 0)
            };

            Assert.AreEqual(ptpExpected.Start, ptpActual.Start);
            Assert.AreEqual(ptpExpected.Finish, ptpActual.Finish);

            // 后面重叠
            ptp = new PeriodTimePortion
            {
                Start = new TimeSpan(12, 0, 0),
                Finish = new TimeSpan(19, 0, 0)
            };

            ptpActual = target.Intersect(ptp);
            ptpExpected = new PeriodTimePortion
            {
                Start = new TimeSpan(12, 0, 0),
                Finish = new TimeSpan(18, 0, 0)
            };

            Assert.AreEqual(ptpExpected.Start, ptpActual.Start);
            Assert.AreEqual(ptpExpected.Finish, ptpActual.Finish);

            // 没有交集
            ptp = new PeriodTimePortion
            {
                Start = new TimeSpan(6, 0, 0),
                Finish = new TimeSpan(7, 0, 0)
            };

            ptpActual = target.Intersect(ptp);
            Assert.IsNull(ptpActual);

            target = new Period
            {
                Start = new DateTime(1, 1, 1, 0, 0, 0),
                Finish = new DateTime(1, 1, 2, 0, 0, 0)
            };
            ptp = new PeriodTimePortion
            {
                Start = TimeSpan.Zero,
                Finish = TimeSpan.MaxValue
            };
            ptpActual = target.Intersect(ptp);
            ptpExpected = new PeriodTimePortion
            {
                Start = new TimeSpan(0, 0, 0),
                Finish = new TimeSpan(0, 0, 0)
            };

            Assert.AreEqual(ptpExpected.Start, ptpActual.Start);
            Assert.AreEqual(ptpExpected.Finish, ptpActual.Finish);
        }

        [TestMethod]
        public void BVT_DbResourceCalendarSerializeTest()
        {
            var calendar = CreateStandardResourceCalendar();
            Assert.IsNotNull(calendar.CalendarSettings);

            var data = calendar.CalendarSettings;
            var actual = new DbResourceCalendar();
            actual.CalendarSettings = data;
       
            AreResourceCalendarEqual(calendar, actual);            
        }

        [TestMethod]
        public void BVT_PersistDbResourceCalendarTest()
        {
            var calendar = new DbResourceCalendar("Standard");
            var workweek = new WorkWeek("Default")
            {
                EffectPeriod = new Period { Start = DateTime.MinValue, Finish = DateTime.MaxValue }
            };

            workweek.Days.Add(new WorkDay(DayOfWeek.Sunday)
            {
                Type = WorkingTimeType.Nonworking
            });

            var am8 = new DateTime(1, 1, 1, 8, 0, 0);
            var pm12 = new DateTime(1, 1, 1, 12, 0, 0);
            var pm1 = new DateTime(1, 1, 1, 13, 0, 0);
            var pm5 = new DateTime(1, 1, 1, 17, 0, 0);

            for (var d = DayOfWeek.Monday; d < DayOfWeek.Saturday; ++d)
            {
                var day = new WorkDay(d)
                {
                    Type = WorkingTimeType.Working
                };

                day.Periods.Add(new Period { Start = am8, Finish = pm12 });
                day.Periods.Add(new Period { Start = pm1, Finish = pm5 });
                workweek.Days.Add(day);
            }

            workweek.Days.Add(new WorkDay(DayOfWeek.Saturday)
            {
                Type = WorkingTimeType.Nonworking
            });

            calendar.WorkWeeks.Add(workweek);

            using (var impl = new VoweiContextImpl())
            {
                var context = new VoweiContext(impl);
                context.ResourceCalendars.Add(calendar);
                context.SaveChanges();
            }

            using (var impl = new VoweiContextImpl())
            {
                var context = new VoweiContext(impl);
                var actual = context.ResourceCalendars.Find(calendar.ID);       
                AreResourceCalendarEqual(calendar, actual);      
            }
        }

        private static void AreResourceCalendarEqual(ResourceCalendar calendar, ResourceCalendar actual)
        {
            Assert.AreEqual(calendar.Name, actual.Name);
            Assert.AreEqual(calendar.Culture, actual.Culture);
            Assert.AreEqual(calendar.WorkWeeks.Count, actual.WorkWeeks.Count);

            var eiter = ((IEnumerable<WorkWeek>)calendar.WorkWeeks).GetEnumerator();
            var aiter = ((IEnumerable<WorkWeek>)calendar.WorkWeeks).GetEnumerator();

            eiter.Reset();
            aiter.Reset();
            while (eiter.MoveNext() && aiter.MoveNext())
            {
                Assert.AreEqual(eiter.Current.Name, aiter.Current.Name);
                Assert.AreEqual(eiter.Current.EffectPeriod.Start, aiter.Current.EffectPeriod.Start);
                Assert.AreEqual(eiter.Current.EffectPeriod.Finish, aiter.Current.EffectPeriod.Finish);
                Assert.AreEqual(eiter.Current.Days.Count, aiter.Current.Days.Count);

                var editer = ((IEnumerable<WorkDay>)eiter.Current.Days).GetEnumerator();
                var aditer = ((IEnumerable<WorkDay>)aiter.Current.Days).GetEnumerator();

                editer.Reset();
                aditer.Reset();

                while (editer.MoveNext() && aditer.MoveNext())
                {
                    var dexpected = editer.Current;
                    var dactual = aditer.Current;

                    Assert.AreEqual(dexpected.Type, dactual.Type);
                    Assert.AreEqual(dexpected.DayOfWeek, dactual.DayOfWeek);
                    Assert.AreEqual(dexpected.Periods.Count, dactual.Periods.Count);

                    for (int i = 0; i < dexpected.Periods.Count; ++i)
                    {
                        var pexpected = dexpected.Periods[i];
                        var pactual = dactual.Periods[i];

                        Assert.AreEqual(pexpected.Start, pactual.Start);
                        Assert.AreEqual(pexpected.Finish, pactual.Finish);
                    }
                }
            }
        }

        [TestMethod]
        public void BVT_CalendarExceptionHolidayTest()
        { 
            // 2011年中国五一假日安排
            var calendar = CreateStandardResourceCalendar();
            var exception = new ExceptionPeriod()
            {
                Type = WorkingTimeType.Nonworking,
                Start = new DateTime(2011, 4, 30), 
                Finish = new DateTime(2011, 5, 3),
                Description = "五一假日安排"
            };
            calendar.Except(exception);
            var calc = new WorkingHourCalculator(calendar);

            Assert.IsFalse(calc.IsWorkingTime(new DateTime(2011, 4, 30)));
            Assert.IsFalse(calc.IsWorkingTime(new DateTime(2011, 5, 1)));
            Assert.IsFalse(calc.IsWorkingTime(new DateTime(2011, 5, 2)));
            Assert.IsFalse(calc.IsWorkingTime(new DateTime(2011, 5, 3)));
            Assert.IsTrue(calc.IsWorkingTime(new DateTime(2011, 5, 3, 8, 0, 0)));
            Assert.AreEqual(TimeSpan.FromHours(8), calc.CalcWorkingHours(new DateTime(2011, 4, 29), new DateTime(2011, 5, 3, 7, 0, 0)));
            Assert.AreEqual(TimeSpan.FromHours(9), calc.CalcWorkingHours(new DateTime(2011, 4, 29), new DateTime(2011, 5, 3, 9, 0, 0)));
        }

        [TestMethod]
        public void BVT_CalendarExceptionEngineerVacationTest()
        {
            var calendar = CreateStandardResourceCalendar();
            var project = CreateProjectHelper("CalendarExceptionEngineerVacationTest");
            project.ProjectBaseCalendar = calendar;
            var engineer = new Employee() { Email = "yishi@vowei.com", UserName = @"vowei\yishi", DisplayName = "施懿民" };
            project.ResourceAssignments.Add(new ResourceAssignment()
            {
                ProjectId = project.ID,
                ResourceId = engineer.ID,
                CostPerHour = 14.3f,
                WorkCalendar = project.ProjectBaseCalendar
            });

            // 请假
            var assignment = project.ResourceAssignments[0];
            assignment.Except(new ExceptionPeriod()
            {
                Type = WorkingTimeType.Nonworking,
                Start = new DateTime(2011, 4, 22, 8, 0, 0),
                Finish = new DateTime(2011, 4, 22, 12, 0, 0),
                Description = "临时事假"
            });
            assignment.Except(new ExceptionPeriod()
            {
                Type = WorkingTimeType.Nonworking,
                Start = new DateTime(2011, 4, 25, 8, 0, 0),
                Finish = new DateTime(2011, 4, 25, 14, 0, 0),
                Description = "临时事假"
            });
            var calc = new WorkingHourCalculator(assignment.WorkCalendar, assignment);

            // 单个员工请假不能影响到项目的工时日历
            Assert.AreEqual(project.ProjectBaseCalendar.ID, assignment.WorkCalendar.ID);
            // 请假是没有计算工时的
            Assert.IsFalse(calc.IsWorkingTime(new DateTime(2011, 4, 22, 8, 0, 0)));
            Assert.IsFalse(calc.IsWorkingTime(new DateTime(2011, 4, 22, 9, 0, 0)));
            Assert.IsFalse(calc.IsWorkingTime(new DateTime(2011, 4, 25, 9, 0, 0)));
            Assert.IsTrue(calc.IsWorkingTime(new DateTime(2011, 4, 22, 13, 0, 0)));
            Assert.IsTrue(calc.IsWorkingTime(new DateTime(2011, 4, 22, 14, 0, 0)));
            Assert.IsTrue(calc.IsWorkingTime(new DateTime(2011, 4, 25, 15, 0, 0)));
            Assert.AreEqual(TimeSpan.FromHours(8), new WorkingHourCalculator(project.ProjectBaseCalendar).CalcWorkingHours(new DateTime(2011, 4, 22)));
            Assert.AreEqual(TimeSpan.FromHours(8), new WorkingHourCalculator(project.ProjectBaseCalendar).CalcWorkingHours(new DateTime(2011, 4, 25)));
            Assert.AreEqual(TimeSpan.FromHours(4), calc.CalcWorkingHours(new DateTime(2011, 4, 22)));
            Assert.AreEqual(TimeSpan.FromHours(3), calc.CalcWorkingHours(new DateTime(2011, 4, 25)));
            Assert.AreEqual(TimeSpan.FromHours(15), calc.CalcWorkingHours(new DateTime(2011, 4, 21), new DateTime(2011, 4, 25, 18, 0, 0)));
        }

        [TestMethod]
        public void BVT_CalendarExceptionEngineerWorkOverTimeTest()
        {
            var calendar = CreateStandardResourceCalendar();
            var project = CreateProjectHelper("CalendarExceptionEngineerWorkOverTime");
            project.ProjectBaseCalendar = calendar;
            var engineer = new Employee() { Email = "yishi@vowei.com", UserName = @"vowei\yishi", DisplayName = "施懿民" };
            project.ResourceAssignments.Add(new ResourceAssignment()
            {
                ProjectId = project.ID,
                ResourceId = engineer.ID,
                CostPerHour = 14.3f,
                WorkCalendar = project.ProjectBaseCalendar
            });

            // 请假
            var assignment = project.ResourceAssignments[0];
            assignment.Except(new ExceptionPeriod()
            {
                Type = WorkingTimeType.Working,
                Start = new DateTime(2011, 5, 6, 19, 0, 0),
                Finish = new DateTime(2011, 5, 6, 22, 0, 0),
                Description = "临时加班"
            });
            var calc = new WorkingHourCalculator(assignment.WorkCalendar, assignment);

            // 单个员工加班不能影响到项目的工时日历
            Assert.AreEqual(project.ProjectBaseCalendar.ID, assignment.WorkCalendar.ID);
            // 加班是计算工时的
            Assert.IsFalse(calc.IsWorkingTime(new DateTime(2011, 5, 6, 7, 0, 0)));
            Assert.IsFalse(calc.IsWorkingTime(new DateTime(2011, 5, 6, 18, 30, 0)));
            Assert.IsFalse(calc.IsWorkingTime(new DateTime(2011, 5, 6, 23, 0, 0)));
            Assert.IsTrue(calc.IsWorkingTime(new DateTime(2011, 5, 6, 19, 0, 0)));
            Assert.IsTrue(calc.IsWorkingTime(new DateTime(2011, 5, 6, 20, 0, 0)));
            Assert.AreEqual(TimeSpan.FromHours(11), calc.CalcWorkingHours(new DateTime(2011, 5, 6)));
        }

        //施 懿民 说：
        //   对了，你上次说的索尼那边的项目
        //   不是有一种情况
        //   一个员工可能被分配到很多项目组的情况吗？
        //   他们是怎么解决这个问题的？
        //翁贞 说：
        //   老样子
        //施 懿民 说：
        //   老样子是什么意思？
        //翁贞 说：
        //   没有解决
        //施 懿民 说：
        //   我现在正在做资源跟项目的整合
        //   所以我想了解一下他们的问题
        //   我的设计里，一个资源（员工）是可以同时被分配到不同的项目组里的
        //   分配到项目组的时候，有一个所谓的工作日历
        //   也就是定义了一个员工在一个项目组的工作时间
        //   不知道这样的方案是否适合他们的情况
        //翁贞 说：
        //   他们的想法就是，在我需要人的时候，我可以一目了然的知道，哪个人在哪个项目组，哪个人什么时间段的有空闲的
        //施 懿民 说：
        //   哦，也就是在一开始的时候，并不知道这个人在哪个项目上
        //   嗯，这是一个好需求
        //   比较符合实际
        [TestMethod]
        public void BVT_BookResourceDesignTest()
        {
            var employee = new Employee { Email = "vie@vowei.com", DisplayName = "Very Important Employee" };
            var resource = new SharedResource(employee, Setting.DefaultResourceCalendar);

            using (var impl = new VoweiContextImpl())
            {
                var context = new VoweiContext(impl);

                if (!context.Employees.Query.Any(ep => ep.Email == employee.Email))
                {
                    context.Employees.Add(employee);
                    context.SaveChanges();
                    context.Resources.Add(resource);
                }

                var projects = new Project[] {
                    CreateProjectHelper("BVT_BookResourceDesignTest Project A", context),
                    CreateProjectHelper("BVT_BookResourceDesignTest Project B", context)
                };

                foreach ( var project in projects )
                    context.Projects.Add(project);

                var task = new Task
                {
                    StartDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day,
                                             10, 0, 0),
                    BelongsToMilestoneId = projects[0].ID
                };
                task.DueDate = task.StartDate + TimeSpan.FromHours(2);
                context.Tasks.Add(task);
                // 员工和设备都应该有一个邮件账号对应
                Assert.IsTrue(resource.AssignTask(task));

                task = new Task
                {
                    StartDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day,
                                             13, 0, 0),
                    BelongsToMilestoneId = projects[1].ID
                };
                task.DueDate = task.StartDate + TimeSpan.FromHours(2);
                context.Tasks.Add(task);
                Assert.IsTrue(resource.AssignTask(task));

                context.SaveChanges();

                Assert.IsTrue(resource.IsBusyAt(task.StartDate.Value + TimeSpan.FromHours(1)));
                Assert.IsFalse(resource.IsBusyAt(task.StartDate.Value + TimeSpan.FromHours(4)));
            }
        }
    }
}
